package file

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	c "xrt/constants"
	"xrt/utils/file/log"
)

type IniMap struct {
	Section map[string]map[string]string //被[]包裹的内容键值
	Kv      map[string]string            //所有的键值对
}

//打印
func (i IniMap) Print() {
	//TODO 完善打印
	fmt.Printf("%v\n", i.Section)
}

func (i IniMap) get(keys []string) []string {
	var values []string
	for _, key := range keys {
		switch strings.Count(key, ".") {
		case 0:
			if _, in := i.Kv[key]; in {
				values = append(values, i.Kv[key])
			} else {
				values = append(values, c.EmptyString)
			}
		case 1:
			st := strings.Split(key, ".")
			if _, in1 := i.Section[st[0]]; in1 {
				if _, in2 := i.Section[st[0]][st[1]]; in2 {
					values = append(values, i.Section[st[0]][st[1]])
				}
			}
			values = append(values, c.EmptyString)
		default:
			values = append(values, c.EmptyString)
		}
	}

	return values
}

func (i IniMap) GetString(kd ...string) string {
	switch len(kd) {
	case 1:
		return i.get(kd)[0]
	case 2:
		if getVal := i.get([]string{kd[0]})[0]; getVal == c.EmptyString {
			return kd[1]
		} else {
			return getVal
		}
	default:
		panic("Arguments number 1 ~ 2.")
	}
}

func (i IniMap) GetStrings(keys ...string) map[string]string {
	kv := map[string]string{}
	for _, key := range keys {
		kv[key] = i.GetString(key)
	}
	return kv
}

func (i IniMap) GetInt64(kd ...interface{}) int64 {
	switch len(kd) {
	case 1:
		if retVal := i.get([]string{kd[0].(string)})[0]; retVal != c.EmptyString {
			if rV, err := strconv.Atoi(retVal); err == nil {
				return int64(rV)
			} else {
				panic(fmt.Sprintf("%s can't cronvert to int64.", kd[0]))
			}
		}
		return 0
	case 2:
		switch reflect.ValueOf(kd[1]).Kind() {
		case reflect.Int, reflect.Int32, reflect.Int64:
		default:
			panic(fmt.Sprintf("%s must be Int.", kd[1]))
		}
		if retVal := i.get([]string{kd[0].(string)})[0]; retVal == c.EmptyString {
			return int64(kd[1].(int))
		} else {
			if rV, err := strconv.Atoi(retVal); err == nil {
				return int64(rV)
			} else {
				panic(fmt.Sprintf("%s can't cronvert to int64.", kd[0]))
			}
		}
	default:
		panic("Arguments number 1 ~ 2.")
	}
}

func (i IniMap) GetBool(kd ...interface{}) bool {
	switch len(kd) {
	case 1:
		if retVal := i.get([]string{kd[0].(string)})[0]; retVal != c.EmptyString {
			if rV, err := strconv.ParseBool(retVal); err == nil {
				return rV
			} else {
				panic(fmt.Sprintf("%s can't cronvert to int64.", kd[0]))
			}
		}
		return false
	case 2:
		switch reflect.ValueOf(kd[1]).Kind() {
		case reflect.Bool:
		default:
			panic(fmt.Sprintf("%s must be Bool.", kd[1]))
		}
		if retVal := i.get([]string{kd[0].(string)})[0]; retVal == c.EmptyString {
			return false
		} else {
			if rV, err := strconv.ParseBool(retVal); err == nil {
				return rV
			} else {
				panic(fmt.Sprintf("%s can't cronvert to int64.", kd[0]))
			}
		}
	default:
		panic("Arguments number 1 ~ 2.")
	}
}

//TODO 保存Ini文件
func (i IniMap) Save(filePath string) error {
	return nil
}

//加载Ini文件
func LoadIni(filePath string) (IniMap, error) {
	iniMap := IniMap{Section: map[string]map[string]string{}, Kv: map[string]string{}}

	sectionReg, err := regexp.Compile(`\[(.+)]`)
	log.CheckError(err)
	if fileContent, err := ioutil.ReadFile(filePath); err != nil {
		return IniMap{}, err
	} else {
		contentRows := bytes.Split(fileContent, []byte(c.LineSep))
		nowSection, key, value := c.EmptyString, c.EmptyString, c.EmptyString
		for _, row := range contentRows {
			//过滤注释行和空行
			if len(row) == 0 || row[0] == '#' || row[0] == ';' {
				continue
				//处理section
			} else if row[0] == '[' {
				nowSection = string(sectionReg.FindSubmatch(row)[1])
				iniMap.Section[nowSection] = map[string]string{}
				continue
				//处理Kv
			} else {
				afterSplit := strings.SplitN(string(row), "=", 2)
				key, value = afterSplit[0], afterSplit[1]
			}

			if nowSection != c.EmptyString {
				iniMap.Section[nowSection][strings.TrimSpace(key)] = strings.TrimSpace(value)
			}
			iniMap.Kv[strings.TrimSpace(key)] = strings.TrimSpace(value)
		}
		return iniMap, nil
	}
}
